package cn.dlc.com.filter;

import cn.dlc.com.config.RsaPropertiesConfig;
import cn.dlc.com.pojo.SysLoginLog;
import cn.dlc.com.pojo.SysPermission;
import cn.dlc.com.pojo.SysRole;
import cn.dlc.com.pojo.SysUser;
import cn.dlc.com.service.SysLoginLogService;
import cn.dlc.com.utils.JsonUtils;
import cn.dlc.com.utils.JwtUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author deng
 * @date 2021/6/25 10:53
 **/
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private RsaPropertiesConfig config;

    // 因为要操作数据库
    private SysLoginLogService loginLogService;


    public JwtLoginFilter(AuthenticationManager authenticationManager, RsaPropertiesConfig config) {
        this.authenticationManager = authenticationManager;
        this.config = config;
    }

    public JwtLoginFilter(AuthenticationManager authenticationManager, RsaPropertiesConfig config, SysLoginLogService loginLogService) {
        this.authenticationManager = authenticationManager;
        this.config = config;
        this.loginLogService = loginLogService;
    }

    // 重写登录认证的方法
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        ObjectMapper objectMapper = new ObjectMapper();

        try {
            // 获取到用户信息
            SysUser sysUser = objectMapper.readValue(request.getInputStream(), SysUser.class);
            if(sysUser == null) {
                // 认证失败
                return null;
            }
            // 进行认证
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                    sysUser.getUsername(), sysUser.getPassword());
            // 认证成功
            return authenticationManager.authenticate(authRequest);

        } catch (Exception e) {
            e.printStackTrace();
            authFail(response);
            // 这一句要写么？
            throw new RuntimeException();
        }
    }

    /**
     * 1、执行attemptAuthentication方法，传递用户名和密码，尽心认证，
     * 2、调用自己的UserDetailsService，进行进一步的认证，同时获取角色和权限
     * 3、认证通过之后重新回到 successfulAuthentication 方法，
     * 4、authResult就是登录后的用户对象
     */
    // 没有实现登出逻辑的代码，因为要增加登出逻辑，所以我们需要在这个地方添加登录后的操作
/*    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        if(authResult != null) {

            SysUser sysUser = (SysUser) authResult.getPrincipal();

            // 生成对应的token
            String token = JwtUtils.generateTokenExpireInMinutes(sysUser, config.getPrivateKey(), 24 * 60);
            // 响应给页面，以头的方式
            response.addHeader("Authorization","Bearer " + token);

            // 认证通过
            authSuccess(response);

        } else {
            authFail(response);
            // 没有权限，
            throw new RuntimeException();
        }
    }*/

    /**
     * 重写这个逻辑，增加登录后操作数据库
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        if(authResult != null) {

            SysUser sysUser = (SysUser) authResult.getPrincipal();
            // 生成对应的token
            String token = JwtUtils.generateTokenExpireInMinutes(sysUser, config.getPrivateKey(), 24 * 60);


            // 将这个用户信息存放到数据库里面。
            SysLoginLog sysLoginLog = new SysLoginLog();
            sysLoginLog.setLoginDate(new Date());
            sysLoginLog.setUsername(sysUser.getUsername());
            sysLoginLog.setToken(token);

            loginLogService.save(sysLoginLog);
            // 还需要将这个用户的其他的登录信息全部设置为过期。这样其实就可以防止单个用户多次登录了。
            // 这里再jwtverifyfilter里面也有对应的验证信息，但是逻辑不对，如果要防止用户多次登录的话，应该还需要重新设计一下这个东西。
            // 想一下如何实现t用户下线




            // 响应给页面，以头的方式
            response.addHeader("Authorization","Bearer " + token);

            // 认证通过
            authSuccess(response);

        } else {
            authFail(response);
            // 没有权限，
            throw new RuntimeException();
        }
    }














    // 认证失败
    private void authFail(HttpServletResponse response){
        try {
            response.setContentType("application/json;charset=UTF-8");
            // 认证失败
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            PrintWriter writer = response.getWriter();

            Map<String,Object> map = new HashMap<>();
            map.put("code",HttpServletResponse.SC_UNAUTHORIZED);
            map.put("msg","用户名或者密码错误");
            writer.write(JsonUtils.toString(map));
            writer.flush();
            writer.close();

        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    // 认证通过
    private void authSuccess(HttpServletResponse response){
        try {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();

            Map<String,Object> map = new HashMap<>();
            map.put("code",HttpServletResponse.SC_OK);
            map.put("msg","认证通过！");
            writer.write(JsonUtils.toString(map));
            writer.flush();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
